//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package data_anno_dao

import (
	"context"
	"fmt"

	"atomgit.com/openlinksaas/api-server/dao/db_ns"
	"atomgit.com/openlinksaas/proto-gen-go.git/data_anno_task_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type TaskInfoDbItem struct {
	TaskId        string `bson:"_id"`
	AnnoProjectId string `bson:"annoProjectId"`
	MemberUserId  string `bson:"memberUserId"`
	ResourceId    string `bson:"resourceId"`
	Done          bool   `bson:"done"`
	TimeStamp     int64  `bson:"timeStamp"`
}

func (item *TaskInfoDbItem) ToTaskInfo() *data_anno_task_api.TaskInfo {
	return &data_anno_task_api.TaskInfo{
		AnnoProjectId: item.AnnoProjectId,
		MemberUserId:  item.MemberUserId,
		ResourceId:    item.ResourceId,
		Done:          item.Done,
	}
}

type _AnnoTaskDao struct {
	mc *mongo.Collection
}

func newAnnoTaskDao(db *mongo.Database) (*_AnnoTaskDao, error) {
	mc := db.Collection(db_ns.DB_NS_DATA_ANNO + "task")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys: bson.D{{"annoProjectId", 1}, {"memberUserId", 1}},
		},
		{
			Keys: bson.D{{"annoProjectId", 1}, {"resourceId", 1}},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_AnnoTaskDao{mc: mc}, nil
}

func (db *_AnnoTaskDao) Insert(ctx context.Context, dbItem *TaskInfoDbItem) error {
	dbItem.TaskId = db.genTaskId(dbItem.AnnoProjectId, dbItem.MemberUserId, dbItem.ResourceId)
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_AnnoTaskDao) genTaskId(annoProjectId, memberUserId, resourceId string) string {
	return fmt.Sprintf("%s:%s:%s", annoProjectId, memberUserId, resourceId)
}

func (db *_AnnoTaskDao) Exist(ctx context.Context, annoProjectId, memberUserId, resourceId string) (bool, error) {
	taskId := db.genTaskId(annoProjectId, memberUserId, resourceId)
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": taskId, "annoProjectId": annoProjectId})
	return count > 0, err
}

func (db *_AnnoTaskDao) CountByAnnoProject(ctx context.Context, annoProjectId string, filterByDone, done bool) (uint32, error) {
	query := bson.M{"annoProjectId": annoProjectId}
	if filterByDone {
		query["done"] = done
	}
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_AnnoTaskDao) CountByMember(ctx context.Context, annoProjectId, memberUserId string, filterByDone, done bool) (uint32, error) {
	query := bson.M{"annoProjectId": annoProjectId, "memberUserId": memberUserId}
	if filterByDone {
		query["done"] = done
	}
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_AnnoTaskDao) CountByResource(ctx context.Context, annoProjectId, resourceId string) (uint32, error) {
	query := bson.M{"annoProjectId": annoProjectId, "resourceId": resourceId}

	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_AnnoTaskDao) RemoveByMember(ctx context.Context, annoProjectId, memberUserId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"annoProjectId": annoProjectId, "memberUserId": memberUserId})
	return err
}

func (db *_AnnoTaskDao) RemoveByResource(ctx context.Context, annoProjectId, resourceId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"annoProjectId": annoProjectId, "resourceId": resourceId})
	return err
}

func (db *_AnnoTaskDao) RemoveByAnnoProject(ctx context.Context, annoProjectId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"annoProjectId": annoProjectId})
	return err
}

func (db *_AnnoTaskDao) DistinctResourceByMember(ctx context.Context, annoProjectId, memberUserId string) ([]string, error) {
	tmpList, err := db.mc.Distinct(ctx, "resourceId", bson.M{"annoProjectId": annoProjectId, "memberUserId": memberUserId})
	if err != nil {
		return nil, err
	}
	resultList := []string{}
	for _, tmpItem := range tmpList {
		resultList = append(resultList, tmpItem.(string))
	}
	return resultList, nil
}

func (db *_AnnoTaskDao) DistinctMemberByResource(ctx context.Context, annoProjectId, resourceId string) ([]string, error) {
	tmpList, err := db.mc.Distinct(ctx, "memberUserId", bson.M{"annoProjectId": annoProjectId, "resourceId": resourceId})
	if err != nil {
		return nil, err
	}
	resultList := []string{}
	for _, tmpItem := range tmpList {
		resultList = append(resultList, tmpItem.(string))
	}
	return resultList, nil
}

func (db *_AnnoTaskDao) ListByMember(ctx context.Context, annoProjectId, memberUserId string, filterByDone, done bool) ([]*TaskInfoDbItem, error) {
	query := bson.M{"annoProjectId": annoProjectId, "memberUserId": memberUserId}
	if filterByDone {
		query["done"] = done
	}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSort(bson.M{"timeStamp": 1}))
	if err != nil {
		return nil, err
	}
	retList := []*TaskInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_AnnoTaskDao) UpdateDone(ctx context.Context, annoProjectId, memberUserId, resourceId string, done bool) error {
	taskId := db.genTaskId(annoProjectId, memberUserId, resourceId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": taskId, "annoProjectId": annoProjectId}, bson.M{"$set": bson.M{"done": done}})
	return err
}
